package com.googlecode.erebos.server;

import com.googlecode.erebos.Player;
import com.sun.sgs.app.*;

import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The MIT License
 * <p/>
 * Copyright (c) 2009 Malachi de AElfweald (malachid@gmail.com)
 * <p/>
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * <p/>
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * <p/>
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
public class PlayerImpl
extends CreatureImpl
implements Player, Serializable
{
    /** The version of the serialized form of this class. */
    private static final long serialVersionUID = 1L;
    
    /** The {@link Logger} for this class. */
    private static final Logger logger = Logger.getLogger(PlayerImpl.class.getName());

    /** The {@code ClientSession} for this player, or null if logged out. */
    private ManagedReference<ClientSession> currentSessionRef = null;

    public PlayerImpl(String name)
    {
        super();
        setName(name);
    }

    @Override
    public void setName(String name)
    {
        super.setName(name);
        bind(BindingPrefix.Player);
    }

    /**
     * Notifies this listener that the specified message, sent by the
     * associated session's client, was received.
     * <p/>
     * <p>If this listener needs to delay processing the given message
     * for any reason, including until more resources become
     * available, this listener may throw {@code MessageRejectedException}.
     * If this listener does throw {@code MessageRejectedException}, it
     * will be as if this invocation never happened; that is, the
     * transaction associated with this invocation is aborted, so it is not
     * possible to save any partial processing result before {@code
     * MessageRejectedException} is thrown.
     *
     * @param    message a message
     * @throws com.sun.sgs.app.MessageRejectedException if there are not enough resources
     * to process the specified message
     */
    public void receivedMessage(ByteBuffer message) {
        String command = decodeString(message);

        logger.log(Level.INFO,
            "{0} received command: {1}",
            new Object[] { this, command }
        );

//        if (command.equalsIgnoreCase("look")) {
//            String reply = getRoom().look(this);
//            getSession().send(encodeString(reply));
//        } else {
            logger.log(Level.WARNING,
                "{0} unknown command: {1}",
                new Object[] { this, command }
            );
//            // We could disconnect the rogue player at this point.
//            //currentSession.disconnect();
//        }
    }

    /**
     * Notifies this listener that the associated session's client has
     * disconnected.  If {@code graceful} is {@code true}, then the
     * session's client logged out gracefully; otherwise, the session
     * was either disconnected forcibly by the server or disconnected
     * due to other factors such as communication failure. <p>
     * <p/>
     * If this listener does not implement {@link com.sun.sgs.app.ManagedObject}, it will
     * be removed from the persistent store after this method returns.
     * Otherwise, this listener will remain in the persistent store until
     * it is explicitly {@linkplain com.sun.sgs.app.DataManager#removeObject removed}. <p>
     * <p/>
     * When this method is invoked, the client session associated with this
     * listener will have already been removed if the application removed
     * the client session in order to disconnect it.  If the client session
     * has not yet been removed and this method does not remove the client
     * session, the client session will be removed in the same transaction
     * after this method returns.
     *
     * @param graceful if {@code true}, the specified client
     *                 session logged out gracefully
     */
    public void disconnected(boolean graceful) {
        setSession(null);
        logger.log(Level.INFO, "Disconnected: {0}", this);
    }

    public static PlayerImpl loggedIn(ClientSession session)
    {
        String binding = BindingPrefix.Player.getName(session.getName());
        DataManager dataMgr = AppContext.getDataManager();
        PlayerImpl player = null;
        try{
            player = (PlayerImpl)dataMgr.getBinding(binding);
        }catch(NameNotBoundException nnbe){
            player = new PlayerImpl(session.getName());
            logger.log(Level.INFO, "New player created: {0}", player);
            dataMgr.setBinding(binding, player);
        }

        player.setSession(session);
        return player;
    }

    /**
     * Returns the session for this listener.
     *
     * @return the session for this listener
     */
    protected ClientSession getSession() {
        if (currentSessionRef == null)
            return null;

        return currentSessionRef.get();
    }

    /**
     * Mark this player as logged in on the given session.
     *
     * @param session the session this player is logged in on
     */
    protected void setSession(ClientSession session) {
        DataManager dataMgr = AppContext.getDataManager();
        dataMgr.markForUpdate(this);

        if(session == null)
            currentSessionRef = null;
        else
            currentSessionRef = dataMgr.createReference(session);

        logger.log(Level.INFO, "Set session for {0} to {1}", new Object[] { this, session });
    }

    /** {@inheritDoc} */
    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder(getName());
        buf.append('@');
        if (getSession() == null) {
            buf.append("null");
        } else {
            buf.append(currentSessionRef.getId());
        }
        return buf.toString();
    }
}
